import java.util.ArrayList;
import java.util.Stack;

public class CSEMachine {

	Stack<ControlElement> control = new Stack<ControlElement>();
	Stack<String> stack = new Stack<String>();
	int envTop = 0;
	ControlStructure ct;
	Environment environment = new Environment();
	Rule1 rule1;
	RuleOperator ruleOp;
	RuleCondition ruleCond;
	RuleLambdaClosure ruleLambdaClosure;
	RuleLambdaApply ruleLambdaAply;
	RuleEnv ruleEnv;
	RuleTupleFormation ruleTupleFrm;
	RuleTupleSelect ruleTupleSelect;
	RulePushY rulePushY;
	RuleApplyY ruleApplyY;
	RuleApplyRecFunc13 ruleApplyRec;

	public CSEMachine(ControlStructure ct) {
		this.ct = ct;
		initRules();
		control.push(new ControlElement("e:0"));
		deltaDump(0);
		envTop++;
		stack.push("e:0");
		environment.envLoaded.push(0);
	}

	private void initRules() {
		rule1 = new Rule1(control, stack, environment);
		ruleOp = new RuleOperator(control, stack, environment);
		ruleCond = new RuleCondition(control, stack, environment, ct);
		ruleLambdaClosure = new RuleLambdaClosure(control, stack, environment);
		ruleLambdaAply = new RuleLambdaApply(control, stack, environment, ct);
		ruleEnv= new RuleEnv(control, stack, environment);
		ruleTupleFrm= new RuleTupleFormation(control, stack, environment);
		ruleTupleSelect = new RuleTupleSelect(control, stack, environment);
		rulePushY = new RulePushY(control, stack, environment);
		ruleApplyY = new RuleApplyY(control, stack, environment);
		ruleApplyRec = new RuleApplyRecFunc13(control, stack, environment);
	}

	public static void main(String[] args) {
		p1 parser = new p1( args[0] );//"c:\\matrix_order1");
		ST st = new ST();

		st.astRoot = parser.getASTTree();
		st.stRoot = st.standardize(st.astRoot);
		// System.out.println("----------");
		// st.stRoot.print(0);

		ControlStructure ct = new ControlStructure(st);
		// System.out.println(ct);

		CSEMachine cse = new CSEMachine(ct);
		cse.start();
	}

	private void start() {
		while (!checkTermination()) {
			
			/*System.out.println("control: " + control);
			System.out.println("stack: " + stack);
			System.out.println("env: " + environment.currentEnv);
			System.out.println();*/
			
			if (rule1.checkRule1()) {
				rule1.applyRule1();
			} else if (ruleOp.checkRule()) {
				ruleOp.applyRule();
			} else if (ruleCond.checkRule()) {
				ruleCond.applyRule();
			} else if (ruleTupleFrm.checkRule()) {
				ruleTupleFrm.applyRule();
			} else if (ruleLambdaClosure.checkRule()){
				ruleLambdaClosure.applyRule();
			} else if (ruleLambdaAply.checkRule()){
				ruleLambdaAply.applyRule();
			} else if (ruleEnv.checkRule()){
				ruleEnv.applyRule();
			} else if (rulePushY.checkRule()){
				rulePushY.applyRule();
			} else if (ruleApplyY.checkRule()){
				ruleApplyY.applyRule();
			} else if (ruleApplyRec.checkRule()){
				ruleApplyRec.applyRule();
			} else if (ruleTupleSelect.checkRule()){
				ruleTupleSelect.applyRule();
			} else{
				System.err.println("I cant handle lot cases yet :)");
				System.exit(0);
			}
			
		}
		System.out.println();
	}

	private void applyRule4() {

	}

	private boolean checkRule4() {
		return false;
	}

	/*
	 * This rule only handles primitive functions, rest all other operators are
	 * optimized
	 */
	private void applyRule3() {
		assert(false):"rule3: this rule need never be applied";
		System.out.println("rule3: this rule need never be applied");
		System.exit(-1);
	}

	/*
	 * gamma -> apply -> result
	 */
	private boolean checkRule3() {
		if (control.peek().content.equals("gamma")
				&& !stack.peek().equals("lambda")
				&& !stack.peek().equals("tau")) {
			return true;
		} else {
			return false;
		}
	}

	/*
	 * remove an top element from stack and check
	 */
	private boolean checkTermination() {
		String holdTop = stack.pop();
		boolean termination = control.peek().content.equals("e:0")
				&& stack.peek().equals("e:0");
		stack.push(holdTop);
		return termination;
	}

	@Override
	public String toString() {
		return "control=" + control + " stack=" + stack + "";
	}

	private void deltaDump(int j) {
		for (int i = 0; i < ct.delta[j].size(); i++) {
			control.push(ct.delta[j].get(i));
		}
	}

}
